package com.skyline.energy.transaction;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class TransactionInterceptor implements MethodInterceptor {
	private static final Log LOGGER = LogFactory.getLog(TransactionInterceptor.class);
	private Map<Method, Method> actualMethods = new HashMap<Method, Method>();
	private Map<Method, TransactionDefinition> txDefinitions = new HashMap<Method, TransactionDefinition>();
	private TransactionManager transactionManager;
	
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		if(transactionManager == null) {
			return invocation.proceed();
		}
		
		Method interfaceMethod = invocation.getMethod();
		Object target = invocation.getThis();
		
		TransactionDefinition txDefinition = getTransactionDefinition(interfaceMethod, target);
		
		if(!txDefinition.isNeedTransaction()) {
			return invocation.proceed();
		}
		
		Object result = null;
		TransactionContext context = null;
		context = transactionManager.begin(txDefinition);
		try {
			result = invocation.proceed();
			transactionManager.commit(context);
		} catch (Throwable ex) {
			if(txDefinition.rollbackOn(ex)) {
				transactionManager.rollback(context);
			}
			throw ex;
		}
		
		return result;
		
	}
	
	private TransactionDefinition getTransactionDefinition(Method interfaceMethod, Object target) {
		Method actualMethod = getActualMethod(interfaceMethod, target);
		if (actualMethod == null) {
			return null;
		}
		TransactionDefinition definition = txDefinitions.get(interfaceMethod);
		if (definition == null) {
			definition = new TransactionDefinition(target, actualMethod);
			txDefinitions.put(interfaceMethod, definition);
		}
		return definition;
	}

	private Method getActualMethod(Method interfaceMethod, Object target) {
		Method actualMethod = actualMethods.get(interfaceMethod);
		if (actualMethod == null) {
			try {
				actualMethod = target.getClass().getMethod(interfaceMethod.getName(),
						interfaceMethod.getParameterTypes());
			} catch (Exception e) {
				LOGGER.warn("无法获取Service中的实现方法", e);
				return null;
			}
			actualMethods.put(interfaceMethod, actualMethod);
		}
		return actualMethod;
	}

	public void setTransactionManager(TransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}
	
}
